package novel.storage.impl;

import novel.spider.entitys.Novel;
import novel.spider.interfaces.INovelSpider;
import novel.spider.util.NovelSpiderFactory;
import novel.storage.Processor;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Created by Kau on 2016/11/26.
 */
public abstract class AbstractNovelStorage implements Processor {
    protected SqlSessionFactory sqlSessionFactory;
    /**
     *
     共同点：
     HashMap,LinkedHashMap,TreeMap都属于Map；Map 主要用于存储键(key)值(value)对，根据键得到值，因此键不允许键重复,但允许值重复。
     不同点：
     1.HashMap里面存入的键值对在取出的时候是随机的,也是我们最常用的一个Map.它根据键的HashCode值存储数据,根据键可以直接获取它的值，具有很快的访问速度。在Map 中插入、删除和定位元素，HashMap 是最好的选择。
     2.TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键，那么TreeMap会更好。
     3. LinkedHashMap 是HashMap的一个子类，如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现.  (应用场景：购物车等需要顺序的)
     */
    protected Map<String, String> tasks = new TreeMap<>();
    public AbstractNovelStorage() throws FileNotFoundException {
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("src/main/resources/SqlMapConfig.xml"));
    }

    public void process() {
        /**
         *
         1. newSingleThreadExecutor
         创建一个单线程的线程池。这个线程池只有一个线程在工作，也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束，那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
         2.newFixedThreadPool
         创建固定大小的线程池。每次提交一个任务就创建一个线程，直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变，如果某个线程因为执行异常而结束，那么线程池会补充一个新线程。
         3. newCachedThreadPool
         创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程，
         那么就会回收部分空闲（60秒不执行任务）的线程，当任务数增加时，此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制，线程池大小完全依赖于操作系统（或者说JVM）能够创建的最大线程大小。
         4.newScheduledThreadPool
         创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
         */
        ExecutorService service = Executors.newFixedThreadPool(tasks.size());
        /**
         * shutdown() 方法在终止前允许执行以前提交的任务，而 shutdownNow() 方法阻止等待任务启动并试图停止当前正在执行的任务。
         */
        List<Future<String>> futures = new ArrayList<>(tasks.size());
        for (Map.Entry<String, String> entry : tasks.entrySet()) {
            final String key = entry.getKey();
            final String value = entry.getValue();
            futures.add(service.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    INovelSpider spider = NovelSpiderFactory.getNovelSpider(value);
                    Iterator<List<Novel>> iterator = spider.iterator(value, 10);
                    //Iterator<List<Novel>> iterators = NovelSpiderFactory.getNovelSpider(value).iterator(value,10);
                    while (iterator.hasNext()) {
                        System.err.println("开始抓取[" + key + "] 的 URL:" + spider.next());
                        //System.err.println("开始抓取[" + key + "] 的 URL:" + iterators.next());
                        List<Novel> novels = iterator.next();
                        for (Novel novel : novels) {
                            novel.setFirstLetter(key.charAt(0)); // 设置小说首字母
                            //System.out.println(novel);
                        }
                        SqlSession session = sqlSessionFactory.openSession();
                        session.insert("batchInsert", novels);
                        session.commit();
                        session.close();
                        Thread.sleep(1_000);
                    }
                    return key;
                }
            }));
        }
        service.shutdown();
        for (Future<String> future : futures) {
            try {
                System.out.println("抓取[" + future.get() + "]结束!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}